﻿// inst.cpp : 定义控制台应用程序的入口点。
//
#include <string>
#include <iostream>
#include <string>
#include <windows.h>
#include <tchar.h>
#include "Shlwapi.h"
#include "Winsvc.h"
#include "Setupapi.h"
#include "VirtualBox.h"
#import  "VirtualBox.tlb" rename_namespace("NBVirtualBox")

int exec(const wchar_t* params)
{
	//runas / user:administrator cmd
	//runas / user:Administrator uninstall.exe
	using namespace std;
	DWORD rc = ERROR_SUCCESS;
	DWORD dwExitCode = 0;
	STARTUPINFO si = { 0 };
	PROCESS_INFORMATION pi = { 0 };

	std::wstring cmdline = params;
	si.cb = sizeof(si);
	si.dwFlags = STARTF_USESHOWWINDOW;
	si.dwFlags |= CREATE_UNICODE_ENVIRONMENT;
	si.wShowWindow = SW_HIDE;
	SetLastError(0);
	if (!CreateProcess(NULL, const_cast<LPWSTR>(cmdline.c_str()), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
	{
		rc = GetLastError();
		return rc;
	}
	WaitForSingleObject(pi.hProcess, INFINITE);
	CloseHandle(pi.hProcess);
	CloseHandle(pi.hThread);
	return rc;
}


int checkcom()
{
	IVirtualBox *gpVirtualBox = NULL;
	IVirtualBoxClient *gpVirtualBoxClient = NULL;
	ISystemProperties *gpSystemProperties = NULL;
	HRESULT rc;
	CoInitialize(NULL);
	rc = CoCreateInstance(CLSID_VirtualBoxClient,
		NULL,
		CLSCTX_INPROC_SERVER,
		IID_IVirtualBoxClient,
		(void**)&gpVirtualBoxClient);

	if (FAILED(rc))
	{
		printf("Error creating VirtualBox instance! rc = 0x%x\n", rc);
		CoUninitialize();
		return -1;
	}
	CoUninitialize();
	return 0;
}
#define SERVICE_NAME    L"Breadvm"

static int suplibOsCreateService(void)
{
	/*
	* Assume it didn't exist, so we'll create the service.
	*/
	int        rc;
	SC_HANDLE hSMgrCreate = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG);
	DWORD     dwErr = GetLastError();
	if (hSMgrCreate != NULL)
	{
		WCHAR driverpath[MAX_PATH] = { 0 };
		GetModuleFileName(NULL, driverpath, MAX_PATH); \

			std::wstring path(driverpath);
		path.erase(path.find_last_of(L"\\/") + 1, path.length());

		memset(driverpath, 0, sizeof(WCHAR)*MAX_PATH);
		PathCombine(driverpath, path.c_str(), L"hyperv\\Breadvm.sys");

		SC_HANDLE hService = CreateService(hSMgrCreate,
			SERVICE_NAME,
			L"Breadvm Support Driver",
			SERVICE_QUERY_STATUS,
			SERVICE_KERNEL_DRIVER,
			SERVICE_AUTO_START,
			SERVICE_ERROR_NORMAL,
			driverpath,
			NULL, NULL, NULL, NULL, NULL);
		dwErr = GetLastError();
		if (hService)
		{
			CloseServiceHandle(hService);
			rc = 0;
		}
		else if (dwErr == ERROR_SERVICE_EXISTS)
			rc = 0;
		else
		{
			rc = dwErr;
		}
		CloseServiceHandle(hSMgrCreate);
	}
	else
		rc = GetLastError();
	return rc;
}


static int suplibOsStartService(void)
{
	/*
	* Check if the driver service is there.
	*/
	SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_QUERY_STATUS | SERVICE_START);
	if (hSMgr == NULL)
	{
		DWORD dwErr = GetLastError();

		return dwErr;
	}

	/*
	* Try open our service to check it's status.
	*/
	SC_HANDLE hService = OpenService(hSMgr, SERVICE_NAME, SERVICE_QUERY_STATUS | SERVICE_START);
	if (!hService)
	{
		/*
		* Create the service.
		*/
		int rc = suplibOsCreateService();
		if (rc != 0)
			return rc;

		/*
		* Try open the service.
		*/
		hService = OpenService(hSMgr, SERVICE_NAME, SERVICE_QUERY_STATUS | SERVICE_START);
	}

	/*
	* Check if open and on demand create succeeded.
	*/
	int rc;
	if (hService)
	{

		/*
		* Query service status to see if we need to start it or not.
		*/
		SERVICE_STATUS Status;
		BOOL fRc = QueryServiceStatus(hService, &Status);
		if (Status.dwCurrentState == SERVICE_RUNNING)
			rc = 0;
		else
		{
			if (Status.dwCurrentState == SERVICE_START_PENDING)
				rc = 0;
			else
			{
				/*
				* Start it.
				*/
				if (StartService(hService, 0, NULL))
					rc = 0;
				else
				{
					DWORD dwErr = GetLastError();
					rc = dwErr;
				}
			}
			/*
			* Wait for the service to finish starting.
			* We'll wait for 10 seconds then we'll give up.
			*/
			QueryServiceStatus(hService, &Status);
			if (Status.dwCurrentState == SERVICE_START_PENDING)
			{
				int iWait;
				for (iWait = 100; iWait > 0 && Status.dwCurrentState == SERVICE_START_PENDING; iWait--)
				{
					Sleep(100);
					QueryServiceStatus(hService, &Status);
				}
				DWORD dwErr = GetLastError();
			}

			if (Status.dwCurrentState == SERVICE_RUNNING)
				rc = 0;
			else rc = -1;
		}

		/*
		* Close open handles.
		*/
		CloseServiceHandle(hService);
	}
	else
	{
		DWORD dwErr = GetLastError();
		rc = dwErr;
	}
	CloseServiceHandle(hSMgr);
	return rc;
}
static int suplibOsStopService(void)
{
	/*
	* Assume it didn't exist, so we'll create the service.
	*/
	int         rc;
	SC_HANDLE   hSMgr = OpenSCManager(NULL, NULL, SERVICE_STOP | SERVICE_QUERY_STATUS);
	DWORD       dwErr = GetLastError();
	if (hSMgr)
	{
		SC_HANDLE hService = OpenService(hSMgr, SERVICE_NAME, SERVICE_STOP | SERVICE_QUERY_STATUS);
		if (hService)
		{
			/*
			* Stop the service.
			*/
			SERVICE_STATUS  Status;
			QueryServiceStatus(hService, &Status);
			if (Status.dwCurrentState == SERVICE_STOPPED)
				rc = 0;
			else if (ControlService(hService, SERVICE_CONTROL_STOP, &Status))
			{
				int iWait = 100;
				while (Status.dwCurrentState == SERVICE_STOP_PENDING && iWait-- > 0)
				{
					Sleep(100);
					QueryServiceStatus(hService, &Status);
				}
				if (Status.dwCurrentState == SERVICE_STOPPED)
					rc = 0;
				else
				{
					rc = 1;
				}
			}
			else
			{
				rc = GetLastError();
			}
			CloseServiceHandle(hService);
		}
		else
		{
			dwErr = GetLastError();
			if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST)
				rc = 0;
			else
			{
				rc = dwErr;
			}
		}
		CloseServiceHandle(hSMgr);
	}
	else
		rc = dwErr;
	return rc;
}

int suplibOsDeleteService(void)
{
	/*
	* Assume it didn't exist, so we'll create the service.
	*/
	int         rc;
	SC_HANDLE   hSMgr = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG);
	DWORD       dwErr = GetLastError();
	if (hSMgr)
	{
		SC_HANDLE hService = OpenService(hSMgr, SERVICE_NAME, DELETE);
		if (hService)
		{
			/*
			* Delete the service.
			*/
			if (DeleteService(hService))
				rc = 0;
			else
			{
				dwErr = GetLastError();
				rc = dwErr;
			}
			CloseServiceHandle(hService);
		}
		else
		{
			dwErr = GetLastError();
			if (dwErr == ERROR_SERVICE_DOES_NOT_EXIST)
				rc = 0;
			else
			{
				rc = dwErr;
			}
		}
		CloseServiceHandle(hSMgr);
	}
	else
		rc = dwErr;
	return rc;
}

int suplibOsInstall(void)
{
	//For example, the following command line runs the DefaultInstall section of the Shell.inf file. 
	//If Setup determines a reboot is required, the user is will be prompted with a "Reboot the computer, Yes/No" dialog box.
	//RUNDLL32.EXE SETUPAPI.DLL, InstallHinfSection DefaultInstall 132 C:\WINDOWS\INF\SHELL.INF
	//	Requirements
	//	Header
	//	Setupapi.h
	//	Library
	//	Setupapi.lib
	//	DLL
	//	Setupapi.dll
	//	Unicode and ANSI names
	//	InstallHinfSectionW(Unicode) and InstallHinfSectionA(ANSI)
	//InstallHinfSection(NULL, NULL, TEXT("DefaultUninstall 132 path-to-uninstall-dir\infname.inf"), 0);
	//InstallHinfSection(NULL, NULL, TEXT("DefaultInstall 132 path-to-inf\infname.inf"), 0);
	int rc = suplibOsCreateService();
	if (rc == 0)
	{
		int rc2 = suplibOsStartService();
		if (rc2 != 0)
			rc = rc2;
	}
	return rc;
}


int suplibOsUninstall(void)
{
	int rc = suplibOsStopService();
	if (rc == 0)
		rc = suplibOsDeleteService();
	return rc;
}

int regcom()
{
	int rc = 0;

	WCHAR cmd[MAX_PATH] = { 0 };
	GetModuleFileName(NULL, cmd, MAX_PATH);

	std::wstring path(cmd);
	path.erase(path.find_last_of(L"\\/") + 1, path.length());

	WCHAR windir[MAX_PATH] = { 0 };
	ExpandEnvironmentStrings(TEXT("%windir%"), windir, MAX_PATH);

	WCHAR filepath[MAX_PATH] = { 0 };
	PathCombine(filepath, path.c_str(), L"hyperv\\BreadvmSVC.exe");

	memset(cmd, 0, sizeof(WCHAR)*MAX_PATH);
	wsprintf(cmd, L"\"%s\" /RegServer", filepath);
	rc = exec(cmd);	if (rc != 0) { return rc; }

	memset(cmd, 0, sizeof(WCHAR)*MAX_PATH);
	memset(filepath, 0, sizeof(WCHAR)*MAX_PATH);
	PathCombine(filepath, path.c_str(), L"hyperv\\x86\\BreadvmClient-x86.dll");
	wsprintf(cmd, L"\"%s\\syswow64\\regsvr32\" /s \"%s\"", windir, filepath);
	rc = exec(cmd);	if (rc != 0) { return rc; }

	memset(cmd, 0, sizeof(WCHAR)*MAX_PATH);
	memset(filepath, 0, sizeof(WCHAR)*MAX_PATH);
	PathCombine(filepath, path.c_str(), L"hyperv\\BreadvmC.dll");
	wsprintf(cmd, L"\"%s\\system32\\regsvr32\" /s \"%s\"", windir, filepath);
	rc = exec(cmd);	if (rc != 0) { return rc; }

	return 0;
}

int unregcom()
{
	int rc = 0;
	WCHAR cmd[MAX_PATH] = { 0 };
	GetModuleFileName(NULL, cmd, MAX_PATH);

	std::wstring path(cmd);
	path.erase(path.find_last_of(L"\\/") + 1, path.length());

	WCHAR windir[MAX_PATH] = { 0 };
	ExpandEnvironmentStrings(TEXT("%windir%"), windir, MAX_PATH);

	memset(cmd, 0, sizeof(WCHAR)*MAX_PATH);
	WCHAR filepath[MAX_PATH] = { 0 };
	PathCombine(filepath, path.c_str(), L"hyperv\\BreadvmSVC.exe");
	wsprintf(cmd, L"\"%s\" /UnregServer", filepath);
	rc = exec(cmd);	if (rc != 0){ return rc; }

	memset(cmd, 0, sizeof(WCHAR)*MAX_PATH);
	memset(filepath, 0, sizeof(WCHAR)*MAX_PATH);
	PathCombine(filepath, path.c_str(), L"hyperv\\x86\\BreadvmClient-x86.dll");
	wsprintf(cmd, L"\"%s\\syswow64\\regsvr32\" /s /u \"%s\"", windir, filepath);
	rc = exec(cmd); if (rc != 0){ return rc; }

	memset(cmd, 0, sizeof(WCHAR)*MAX_PATH);
	memset(filepath, 0, sizeof(WCHAR)*MAX_PATH);
	PathCombine(filepath, path.c_str(), L"hyperv\\BreadvmC.dll");
	wsprintf(cmd, L"\"%s\\system32\\regsvr32\" /s /u \"%s\"", windir, filepath);
	rc = exec(cmd); if (rc != 0){ return rc; }

	return 0;
}

void Clean()
{
	WCHAR cmd[MAX_PATH] = { 0 };
	WCHAR windir[MAX_PATH] = { 0 };
	ExpandEnvironmentStrings(TEXT("%windir%"), windir, MAX_PATH);

	memset(cmd, 0, sizeof(WCHAR)*MAX_PATH);
	wsprintf(cmd, L"\"%s\\system32\\TASKKILL\" /F /IM Luncher.exe /T", windir);
	exec(cmd);

	memset(cmd, 0, sizeof(WCHAR)*MAX_PATH);
	wsprintf(cmd, L"\"%s\\system32\\TASKKILL\" /F /IM Breadless.exe /T", windir);
	exec(cmd);

	memset(cmd, 0, sizeof(WCHAR)*MAX_PATH);
	wsprintf(cmd, L"\"%s\\system32\\TASKKILL\" /F /IM BreadvmSVC.exe /T", windir);
	exec(cmd);

	memset(cmd, 0, sizeof(WCHAR)*MAX_PATH);
	wsprintf(cmd, L"\"%s\\system32\\TASKKILL\" /F /IM BreadvmVMMPreload.exe /T", windir);
	exec(cmd);

	memset(cmd, 0, sizeof(WCHAR)*MAX_PATH);
	wsprintf(cmd, L"\"%s\\system32\\TASKKILL\" /F /IM BreadvmNetNAT.exe /T", windir);
	exec(cmd);

	return;
}

//void installinf()
//{
//	WCHAR cmd[MAX_PATH] = { 0 };
//	WCHAR filepath[MAX_PATH] = { 0 };
//	GetModuleFileName(NULL, cmd, MAX_PATH);
//
//	std::wstring path(cmd);
//	path.erase(path.find_last_of(L"\\/") + 1, path.length());
//	PathCombine(filepath, path.c_str(), L"hyperv\\Breadvm.inf");
//	wsprintf(cmd, L"DefaultInstall 132 %s", filepath);
//	//Setupapi.h 
//	//Setupapi.lib
//	//RUNDLL32.EXE SETUPAPI.DLL,InstallHinfSection DefaultUninstall 132 path-to-uninstall-dir\infname.inf
//	InstallHinfSection(NULL, NULL, cmd, 0);
//}
//
//
//void uninstallinf()
//{
//	WCHAR cmd[MAX_PATH] = { 0 };
//	WCHAR filepath[MAX_PATH] = { 0 };
//	GetModuleFileName(NULL, cmd, MAX_PATH);
//
//	std::wstring path(cmd);
//	path.erase(path.find_last_of(L"\\/") + 1, path.length());
//	PathCombine(filepath, path.c_str(), L"hyperv\\Breadvm.inf");
//	wsprintf(cmd, L"DefaultUninstall 132 %s", filepath);
//	//Setupapi.h 
//	//Setupapi.lib
//	InstallHinfSection(NULL, NULL, cmd, 0);
//}

void RegistVM()
{
	IVirtualBox *gpVirtualBox = NULL;
	IVirtualBoxClient *gpVirtualBoxClient = NULL;
	ISystemProperties *gpSystemProperties = NULL;
	HRESULT rc;

	CoInitialize(NULL);
	rc = CoCreateInstance(CLSID_VirtualBoxClient,
		NULL,
		CLSCTX_INPROC_SERVER,
		IID_IVirtualBoxClient,
		(void**)&gpVirtualBoxClient);

	if (FAILED(rc))
	{
		printf("com failed step1 rc = %d\n",rc);
		exit(1);
		return;
	}
	rc = gpVirtualBoxClient->get_VirtualBox((IVirtualBox**)&gpVirtualBox);
	if (FAILED(rc))
	{
		printf("com failed step2 rc = %d\n", rc);
		exit(1);
	}
	rc = gpVirtualBox->get_SystemProperties((ISystemProperties**)&gpSystemProperties);
	if (FAILED(rc))
	{
		printf("com failed step3 rc = %d\n", rc);
		exit(1);
	}
	WCHAR cmd[MAX_PATH] = { 0 };
	GetModuleFileName(NULL, cmd, MAX_PATH);
	std::wstring path(cmd);
	path.erase(path.find_last_of(L"\\/") + 1, path.length());

	WCHAR filepath[MAX_PATH] = { 0 };
	PathCombine(filepath, path.c_str(), L"Breadvm_VMs");

	//ISystemProperties IVirtualBox systemProperties
	//wstring ISystemProperties::defaultMachineFolder
	//IMachine IVirtualBox::openMachine([in] wstring settingsFile)
	rc = gpSystemProperties->put_DefaultMachineFolder(SysAllocString(filepath));
	if (FAILED(rc))
	{
		printf("com failed step4 rc = %d\n", rc);
	}

	memset(cmd, 0, sizeof(WCHAR)*MAX_PATH);
	WCHAR filepath1[MAX_PATH] = { 0 };
	WCHAR filepath2[MAX_PATH] = { 0 };
	PathCombine(filepath1, path.c_str(), L"hyperv\\BreadManage.exe");
	PathCombine(filepath2, path.c_str(), L"Breadvm_VMs\\default\\default.vbox");
	wsprintf(cmd, L"\"%s\" registervm \"%s\"", filepath1, filepath2);
	rc = exec(cmd);
}

void UnregistVM()
{
	WCHAR cmd[MAX_PATH] = { 0 };
	WCHAR filepath[MAX_PATH] = { 0 };
	GetModuleFileName(NULL, cmd, MAX_PATH);
	std::wstring path(cmd);
	path.erase(path.find_last_of(L"\\/") + 1, path.length());
	memset(cmd, 0, sizeof(WCHAR)*MAX_PATH);
	PathCombine(filepath, path.c_str(), L"hyperv\\BreadManage.exe");
	wsprintf(cmd, L"\"%s\" unregistervm \"default\"", filepath);
	exec(cmd);
}

void oleaut32Repair()
{
	WCHAR cmd[MAX_PATH] = { 0 };
	WCHAR filepath[MAX_PATH] = { 0 };
	GetModuleFileName(NULL, cmd, MAX_PATH);
	std::wstring path(cmd);
	path.erase(path.find_last_of(L"\\/") + 1, path.length());
	memset(cmd, 0, sizeof(WCHAR)*MAX_PATH);
	PathCombine(filepath, path.c_str(), L"oleaut32Repair.bat");
	exec(filepath);
}
int _tmain(int argc, _TCHAR* argv[])
{
	
	int rc = 0;
	if (argc == 2 && !wcscmp(argv[1], L"-u"))//uninstall
	{
		CoInitialize(NULL);
		printf("uninstall\n");
		Clean();
		UnregistVM();
		rc = unregcom();
		printf("uninstall rc = %d\n", rc);
		//installinf();
		rc = suplibOsUninstall();
	}
	else//install
	{
		printf("install\n");
		Clean();
		rc = regcom();
		printf("regcom rc = %d\n", rc);
		if (rc == 2) printf("not find file\n");
		rc = checkcom();
		printf("checkcom rc = %d\n", rc);
		if (rc != 0)
		{
			oleaut32Repair();
			printf("try repair reg rc \n");
			rc = regcom();
			rc = checkcom();
			printf("checkcom rc = %d\n", rc);
			if (rc != 0)
			{
				return -1;
			}
		}		
		rc = suplibOsInstall();
		RegistVM();
		printf("DriverInstall rc = %d\n", rc);
	}
	return rc;
}

